
package panels;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.Hashtable;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import main.Icons;

/**
 *
 * @author Raed Shomali
 */
public class COPSEMOGraphPanel extends JPanel implements ChangeListener, ActionListener
{
    // COPSEMO Variables
    private double inceptionPersonnel = 3;
    private double elaborationPersonnel = 4;
    private double constructionPersonnel = 5;
    private double transitionPersonnel = 4;
    
    private double inceptionMonths = 2;
    private double elaborationMonths = 2;
    private double constructionMonths = 2;
    private double transitionMonths = 2;
    
    // For Designing
    private double inceptionStart = 0;
    private double elaborationStart = 0;
    private double constructionStart = 0;
    private double transitionStart = 0;
    
    private double inceptionEnd = 0;
    private double elaborationEnd = 0;
    private double constructionEnd = 0;
    private double transitionEnd = 0;

    public Color inceptionColor = Color.RED ;
    public Color elaborationColor = Color.BLUE ;
    public Color constructionColor = Color.decode( "#006600" ) ;
    public Color transitionColor = Color.decode( "#660066" ) ;
    
    // Starting Coordinates
    int topDesignYPoint = 110;
    int leftDesignXPoint = 70;
    
    int maximumX = 0;
    int maximumY = 0;

    DecimalFormat numberFormat = new DecimalFormat( "#.#" ) ;
    
    JSlider slider = new JSlider( 10 , 100 , 100 ) ;

    JButton zoomInButton = new JButton() ;
    JButton zoomOutButton = new JButton() ;
    
    boolean isLoading = false ;
    
    public void setIsLoading( boolean isLoading )
    {
        this.isLoading = isLoading ;
    }
    
    public COPSEMOGraphPanel()
    {
        slider.addChangeListener( this );
        
        this.setLayout( null );
        
        // Slider
        Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
        
        labelTable.put( new Integer( 10 ), new JLabel( "10%" ) );
        labelTable.put( new Integer( 20 ), new JLabel( "20%" ) );
        labelTable.put( new Integer( 30 ), new JLabel( "30%" ) );
        labelTable.put( new Integer( 40 ), new JLabel( "40%" ) );
        labelTable.put( new Integer( 50 ), new JLabel( "50%" ) );
        labelTable.put( new Integer( 60 ), new JLabel( "60%" ) );
        labelTable.put( new Integer( 70 ), new JLabel( "70%" ) );
        labelTable.put( new Integer( 80 ), new JLabel( "80%" ) );
        labelTable.put( new Integer( 90 ), new JLabel( "90%" ) );
        labelTable.put( new Integer( 100 ), new JLabel( "100%" ) );
        
        slider.setToolTipText( "Zoom Level" ) ;
        slider.setLabelTable( labelTable );

        slider.setPaintLabels( true );
        slider.setSnapToTicks( true );
        slider.setPaintTicks( true );
        slider.setPaintTrack( true );
        
        slider.setMinorTickSpacing( 1 );
        slider.setMajorTickSpacing( 10 );
        
        // Buttons
        zoomInButton.addActionListener( this );
        zoomOutButton.addActionListener( this );
        
        zoomInButton.setToolTipText( "Zoom In" ) ;
        zoomOutButton.setToolTipText( "Zoom Out" ) ; 
        
        zoomInButton.setFocusable( false ) ;
        zoomOutButton.setFocusable( false ) ;
        
        zoomInButton.setIcon( Icons.ZOOM_IN_ICON ) ;
        zoomOutButton.setIcon( Icons.ZOOM_OUT_ICON ) ;
        
        // GUI
        this.add( slider );
        this.add( zoomInButton ) ;
        this.add( zoomOutButton ) ;
        
        zoomOutButton.setBounds( leftDesignXPoint , 10, 25 , 25 );
        slider.setBounds( leftDesignXPoint + 25 , 10 , 400 , 50 );
        zoomInButton.setBounds( leftDesignXPoint + 425 , 10, 25 , 25 );
    }
    
    /**
     * 
     * @return an integer that represents the furthest X Coordinate for resizing
     */
    public int getFurtherestXCoordinate()
    {
        return leftDesignXPoint + maximumX * 50 + 80 ;
    }
    
    /**
     * 
     * @return an integer that represents the furthest Y Coordinate for resizing
     */
    public int getFurthestYCoordinate()
    {
        return  topDesignYPoint + maximumY * 25 + 120  ;
    }
    
    @Override
    public void paintComponent( Graphics g )
    {
        // Clear
        super.paintComponent( g );

        prepareStartEndValues() ;

        // Get Highest Values To Prepare the Graph
        maximumY = maximum( inceptionPersonnel * slider.getValue() / 100 , elaborationPersonnel * slider.getValue() / 100 , constructionPersonnel * slider.getValue() / 100 , transitionPersonnel * slider.getValue() / 100 ) + 1;
        maximumX = maximum( inceptionEnd * slider.getValue() / 100 , elaborationEnd * slider.getValue() / 100 , constructionEnd * slider.getValue() / 100 , transitionEnd * slider.getValue() / 100 ) + 1;

        this.setPreferredSize( new Dimension( getFurtherestXCoordinate() , getFurthestYCoordinate() ) ) ;
        this.revalidate();
        
        // If There Is Something To Draw
        if ( !isLoading && maximumY != 1 && maximumX != 1 )
        {   
            drawGrid( g );

            // Inception
            drawLines( g , inceptionColor ,  inceptionStart * slider.getValue() / 100 , inceptionEnd * slider.getValue() / 100 , inceptionPersonnel * slider.getValue() / 100  );
            
            // Elaboration
            drawLines( g , elaborationColor ,  elaborationStart * slider.getValue() / 100 , elaborationEnd * slider.getValue() / 100 , elaborationPersonnel * slider.getValue() / 100  );
            
            // Construction
            drawLines( g , constructionColor ,  constructionStart * slider.getValue() / 100 , constructionEnd * slider.getValue() / 100 , constructionPersonnel * slider.getValue() / 100  );
            
            // Transition
            drawLines( g , transitionColor ,  transitionStart * slider.getValue() / 100 , transitionEnd * slider.getValue() / 100 , transitionPersonnel * slider.getValue() / 100  );
        }
        else if ( isLoading )
        {
            g.drawString( "Loading Graph ..." , leftDesignXPoint , topDesignYPoint );
        }
        else
        {
            g.drawString( "Proper Parameters were not yet passed to Generate COPSEMO." , leftDesignXPoint , topDesignYPoint ) ;
        }
    }

    /**
     * 
     * @param g is the Graphics object used to draw on the panel
     * @param color is used to determine the color of whats being drawn on the panel
     * @param start is the starting point of the X Coordinate
     * @param end is the ending point of the X Coordinate
     * @param y is the highest value of the Y Coordinate
     */
    public void drawLines( Graphics g , Color color , double start , double end , double y )
    {
        g.setColor( color ) ;
        
        // Start
        g.drawLine( leftDesignXPoint + (int) (start * 50) ,
                topDesignYPoint + maximumY * 25 ,
                leftDesignXPoint + (int) (start * 50) ,
                topDesignYPoint + maximumY * 25 - (int) (y * 25) );

        // End
        g.drawLine( leftDesignXPoint + (int) (end * 50) ,
                topDesignYPoint + maximumY * 25 ,
                leftDesignXPoint + (int) (end * 50) ,
                topDesignYPoint + maximumY * 25 - (int) (y * 25) );

        // Y
        g.drawLine( leftDesignXPoint + (int) (start * 50) ,
                topDesignYPoint + maximumY * 25 - (int) (y * 25) ,
                leftDesignXPoint + (int) (end * 50) ,
                topDesignYPoint + maximumY * 25 - (int) (y * 25) );
        
        g.setColor( new Color( color.getRed() , color.getGreen() , color.getBlue() , 50 ) ) ;
        g.fillRect( leftDesignXPoint + (int) (start * 50) , topDesignYPoint + maximumY * 25 - (int) (y * 25) , (int)( ( end - start ) * 50 ) , (int)( y * 25 ) );
    }
    
    /**
     * 
     * @param g is the Graphics object used to draw on the panel
     */
    public void drawGrid( Graphics g )
    {   
        g.setColor( Color.BLACK );

        g.drawString( "Phased Schedule (M) and Staff (P):" , leftDesignXPoint + 10 , topDesignYPoint - 10  ) ;
        
        // Left Y Axis
        g.drawLine( leftDesignXPoint , topDesignYPoint , leftDesignXPoint , topDesignYPoint + maximumY * 25 );

        g.drawString( "(P)" , leftDesignXPoint - 30 , topDesignYPoint + 5 );
        
        for ( int i = maximumY ; i > 0 ; i-- )
        {
            // Numbers
            g.setColor( Color.BLACK );
            g.drawString( Double.parseDouble( numberFormat.format( (double) (maximumY - i) * 100 / slider.getValue() ) ) + "" , leftDesignXPoint - 30 , topDesignYPoint + i * 25 + 5 );

            // Black With Transperancy
            g.setColor( new Color( 0 , 0 , 0 , 50 ) );
            g.drawLine( leftDesignXPoint , topDesignYPoint + i * 25 , leftDesignXPoint + maximumX * 50 , topDesignYPoint + i * 25 );
        }

        // Back to Default
        g.setColor( Color.BLACK );

        // Right Y Axis
        g.drawLine( leftDesignXPoint + maximumX * 50 , topDesignYPoint , leftDesignXPoint + maximumX * 50 , topDesignYPoint + maximumY * 25 );

        // Top X Axis
        g.drawLine( leftDesignXPoint , topDesignYPoint , leftDesignXPoint + maximumX * 50 , topDesignYPoint );

        // Bottom X Axis
        g.drawLine( leftDesignXPoint , topDesignYPoint + maximumY * 25 , leftDesignXPoint + maximumX * 50 , topDesignYPoint + maximumY * 25 );

        g.drawString( "(M)" , leftDesignXPoint + maximumX * 50 - 8 , topDesignYPoint + maximumY * 25 + 25 );
        
        for ( int i = 0 ; i < maximumX ; i++ )
        {
            // Numbers
            g.setColor( Color.BLACK );
            g.drawString( Double.parseDouble( numberFormat.format( (double) i  * 100 / slider.getValue() ) ) + "" , leftDesignXPoint + i * 50 - 8 , topDesignYPoint + maximumY * 25 + 25 );

            // Black With Transperancy
            g.setColor( new Color( 0 , 0 , 0 , 50 ) );
            g.drawLine( leftDesignXPoint + i * 50 , topDesignYPoint , leftDesignXPoint + i * 50 , topDesignYPoint + maximumY * 25 );
        }
        
        // Legend
        
        g.setColor( Color.DARK_GRAY ) ;
        g.drawRoundRect( leftDesignXPoint , topDesignYPoint + maximumY * 25 + 45 , 325 , 50 , 20 , 20 ) ;
        
        // Inception
        g.setColor( inceptionColor );
        g.drawString( "Inception" , leftDesignXPoint + 15 , topDesignYPoint + maximumY * 25 + 65 );
        
        g.drawRect( leftDesignXPoint + 25 , topDesignYPoint + maximumY * 25 + 75 , 25 , 12 ) ;
        g.setColor( new Color( inceptionColor.getRed() , inceptionColor.getGreen() , inceptionColor.getBlue() , 50 ) ) ;
        g.fillRect( leftDesignXPoint + 25 , topDesignYPoint + maximumY * 25 + 75 , 25 , 12 ) ;
        
        // Elaboration
        g.setColor( elaborationColor );        
        g.drawString( "Elaboration" , leftDesignXPoint + 85 , topDesignYPoint + maximumY * 25 + 65 );

        g.drawRect( leftDesignXPoint + 105 , topDesignYPoint + maximumY * 25 + 75 , 25 , 12 ) ;
        g.setColor( new Color( elaborationColor.getRed() , elaborationColor.getGreen() , elaborationColor.getBlue() , 50 ) ) ;
        g.fillRect( leftDesignXPoint + 105 , topDesignYPoint + maximumY * 25 + 75 , 25 , 12 ) ;
        
        // Construction
        g.setColor( constructionColor );
        g.drawString( "Construction" , leftDesignXPoint + 165 , topDesignYPoint + maximumY * 25 + 65 );

        g.drawRect( leftDesignXPoint + 185 , topDesignYPoint + maximumY * 25 + 75 , 25 , 12 ) ;
        g.setColor( new Color( constructionColor.getRed() , constructionColor.getGreen() , constructionColor.getBlue() , 50 ) ) ;
        g.fillRect( leftDesignXPoint + 185 , topDesignYPoint + maximumY * 25 + 75 , 25 , 12 ) ;
        
        // Transition
        g.setColor( transitionColor );
        g.drawString( "Transition" , leftDesignXPoint + 255 , topDesignYPoint + maximumY * 25 + 65 );  

        g.drawRect( leftDesignXPoint + 265 , topDesignYPoint + maximumY * 25 + 75 , 25 , 12 ) ;
        g.setColor( new Color( transitionColor.getRed() , transitionColor.getGreen() , transitionColor.getBlue() , 50 ) ) ;
        g.fillRect( leftDesignXPoint + 265 , topDesignYPoint + maximumY * 25 + 75 , 25 , 12 ) ;
        
        // Credits
        g.setColor( Color.LIGHT_GRAY ) ;
        g.drawString( "Raed Shomali" , leftDesignXPoint + 350 , topDesignYPoint + maximumY * 25 + 75 ); 
    }

    /**
     * 
     * @param value is used to set the Inception Personnel
     */
    public void setInceptionPersonnel( double value )
    {
        inceptionPersonnel = value;
    }

    /**
     * 
     * @param value is used to set the Elaboration Personnel
     */
    public void setElaborationPersonnel( double value )
    {
        elaborationPersonnel = value;
    }

    /**
     * 
     * @param value is used to set the Construction Personnel
     */
    public void setConstructionPersonnel( double value )
    {
        constructionPersonnel = value;
    }

    /**
     * 
     * @param value is used to set the Transition Personnel
     */
    public void setTransitionPersonnel( double value )
    {
        transitionPersonnel = value;
    }

    /**
     * 
     * @param value is used to set Inception Months
     */
    public void setInceptionMonths( double value )
    {
        inceptionMonths = value;
    }

    /**
     * 
     * @param value is used to set Elaboration Months
     */
    public void setElaborationMonths( double value )
    {
        elaborationMonths = value;
    }

    /**
     * 
     * @param value is used to set Construction Months
     */
    public void setConstructionMonths( double value )
    {
        constructionMonths = value;
    }

    /**
     * 
     * @param value is used to set the Transition Months
     */
    public void setTransitionMonths( double value )
    {
        transitionMonths = value;
    }

    private void prepareStartEndValues()
    {
        // Begins at Zero
        inceptionStart = 0 ;
        inceptionEnd = inceptionStart + inceptionMonths ;
        
        // Starts where Inception Ended
        elaborationStart = inceptionEnd ;
        elaborationEnd = elaborationStart + elaborationMonths ;
        
        // Starts where elaboration Ended
        constructionStart = elaborationEnd ;
        constructionEnd = constructionStart + constructionMonths ;
        
        // Starts where construction ended
        transitionStart = constructionEnd ;
        transitionEnd = transitionStart + transitionMonths ;
    }
    
    private int maximum( double x1 , double x2 , double x3 , double x4 )
    {
        return (int) Math.round( Math.max( Math.max( x1 , x2 ) , Math.max( x3 , x4 ) ) );
    }

    public void stateChanged( ChangeEvent e )
    {
        // Refresh
        repaint() ;
    }

    public void actionPerformed( ActionEvent e )
    {
        if( e.getSource() == zoomInButton )
        {
            // Calculate new value
            int newValue = (int) Math.ceil( slider.getValue() + 10 ) ;
            
            // Validate new Value before setting it
            slider.setValue( newValue > 100 ? 100 : newValue  ) ; 
        }
        else
        {
            // Calculate new value
            int newValue = (int) Math.floor( slider.getValue() - 10 ) ;
            
            // Validate new value before setting it
            slider.setValue( newValue < 0 ? 0 : newValue ) ;
        }
        
        // Refresh
        repaint() ;
    }
}
